#include <stdio.h>
#include <stdint.h>

#include <ZLog.h>
#include <ZMemPool.h>
#include <ZUtils.h>
#include <RTPBase.h>

#include <PcapParser.h>
#include <RsFec.h>
#include <RS_fec_wrapper.h>

#define DST_IP 0xef0b004d /* 239.11.0.77 */
#define DST_PORT_DATA 5140
#define DST_PORT_FEC 5139
#define PACKET_TYPE_IP 0x0800 /* IP Packet */ 
#define TRANS_UDP  0x11

#define TC_SEQ_BEGIN 13213
#define TC_SEQ_END 13312

#define SEQ_DROP 1

extern int parseFecPacket(RTPPacketT *fecPkt, FecInfoT *fecInfo);

int TC_DropNull(MemPoolT *pool, RTPPacketT **dataPacket, RTPPacketT **fecPacket)
{
    RTPGroupT *group = NULL;
    int i, ret;
    
    group = RTPGroupCreate(pool, fecPacket[0]);
    Z_ASSERT(group);
    for (i = 0; i < 100; i++)
    {
        ret = RTPGroupAddData(group, dataPacket[i]);
        Z_ASSERT(ret == 0);
    }
    
    for (i = 0; i < 5; i++)
    {
        ret = RTPGroupAddFec(group, fecPacket[i]);
        Z_ASSERT(ret == 0);
    }

    if (RTPGroupIsCorrect(group))
    {
        LogE("Should not do correct process.");
        return -1;
    }
    
    return 0;
}

int TC_DropDataNo2(MemPoolT *pool, RTPPacketT **dataPacket, RTPPacketT **fecPacket)
{
    RTPGroupT *group = NULL;
    int i, ret;
    
    group = RTPGroupCreate(pool, fecPacket[0]);
    Z_ASSERT(group);
    for (i = 0; i < 100; i++)
    {
        if (i != 1)
        {
            ret = RTPGroupAddData(group, dataPacket[i]);
            Z_ASSERT(ret == 0);
        }
    }
    
    for (i = 0; i < 5; i++)
    {
        ret = RTPGroupAddFec(group, fecPacket[i]);
        Z_ASSERT(ret == 0);
    }

    if (!RTPGroupIsCorrect(group))
    {
        LogE("Not do correct process.");
        return -1;
    }

    RTPPacketT *recoverPacket;
    recoverPacket = ZListEntry(dataPacket[0]->groupNode.next, RTPPacketT, groupNode);
    LogI("Recover-Packet: '%p'", recoverPacket);
    
    if (memcmp(recoverPacket->data, dataPacket[1]->data, dataPacket[1]->dataLen) != 0)
    {
        LogE("Correct error.");
        return -1;
    }
    
    return 0;
}

int TC_RSDecodeRFW(RTPPacketT **pDataPacket, RTPPacketT **pFecPacket)
{
    uint8_t *data[100];
    uint8_t *fec[5];
    uint8_t *dropData;
    int lostMap[105] = {0};
    int i;
    FecInfoT fecInfo;
    RsFecWrapperT *rfw;
    int ret;
    
    parseFecPacket(pFecPacket[0], &fecInfo);

    rfw = RFWCreate(100, 5, fecInfo.rtpLen);

    /* encode */
    for (i = 0; i < 100; i++)
    {
        data[i] = pDataPacket[i]->data;
        lostMap[i] = 1;
    }

    for (i = 0; i < 5; i++)
    {
        fec[i] = malloc(fecInfo.rtpLen);
        memset(data[i], 0x00, fecInfo.rtpLen);
        lostMap[100 + i] = 1;
    }

    ret = RFWEncode(rfw, data, fec);
    if (ret != 0)
    {
        LogE("RS-Encode API failure.");
        return -1;
    }

    dropData = data[SEQ_DROP];
    
    data[SEQ_DROP] = malloc(fecInfo.rtpLen);
    memset(data[SEQ_DROP], 0x00, fecInfo.rtpLen);
    lostMap[SEQ_DROP] = 0;

    ret = RFWDecode(rfw, data, fec, lostMap);
    if (ret != 0)
    {
        LogE("RS-Decode API failure.");
        return -1;
    }

    if (memcmp(data[SEQ_DROP], dropData, fecInfo.rtpLen) != 0)
    {
        LogE("Correct error.");
        return -1;
    }

    return 0;
}

int TC_RSDecodeOrg(RTPPacketT **pDataPacket, RTPPacketT **pFecPacket)
{
    uint8_t *data[100];
    uint8_t *fec[5];
    int lostMap[105] = {0};
    int i;
    FecInfoT fecInfo;
    RsFecT *rf;
    int ret;
    
    parseFecPacket(pFecPacket[0], &fecInfo);

    for (i = 0; i < 100; i++)
    {
        if (i != SEQ_DROP)
        {
            data[i] = pDataPacket[i]->data;
            lostMap[i] = 1;
        }
        else
        {
            data[i] = malloc(fecInfo.rtpLen);
            lostMap[i] = 0;
        }
    }

    for (i = 0; i < 5; i++)
    {
        fec[i] = pFecPacket[i]->data;
        lostMap[100 + i] = 1;
    }
    
    rf = RsFec_Create();
    RsFec_Init(rf, 100, 5, fecInfo.rtpLen);
    ret = RsFec_Decode(rf, data, fec, lostMap);

    if (ret != 0)
    {
        LogE("RS-Decode API failure.");
        return -1;
    }

    if (memcmp(data[SEQ_DROP], pDataPacket[SEQ_DROP]->data, fecInfo.rtpLen) != 0)
    {
        LogE("Correct error.");
        return -1;
    }

    return 0;
}

int main(int argc, char *argv[])
{
    Z_ASSERT(argc == 2);

    char *pcapFile = argv[1];
    
    RTPPacketT *dataPacket[100] = {NULL};
    RTPPacketT *fecPacket[5] = {NULL};
    int indexData = 0;
    int indexFec = 0;
    
    int i = 0, ret;
    PcapT *pcap;

    PcapPacketT *packet;
    uint16_t packetType;

    const int offsetNet = 14; /* IP Layer */
    uint8_t netVersion;
    int netHdrLen;
    uint32_t dstIP;
    uint8_t transProtocol;
    
    int offsetTrans; /* TCP/UDP */
    uint16_t dstPort;
    
    int offsetApp; /* RTP */

    MemPoolT *pool;
    pool = MemPoolCreate(NULL);
    
    pcap = PcapCreate(pcapFile);
    uint16_t seqExpect = 0;
    
    for (i = 0; i < 300; i++)
    {
        ret = PcapFetchPacket(pcap, &packet);
        if (ret != 0)
        {
            LogE("Fetch packet failure...");
            break;
        }

        packetType = GetBE16(packet->data + 12);
        if (PACKET_TYPE_IP != packetType)
        {
            /* not ip packet */
            continue;
        }

        uint8_t byte = 0;
        byte = GetU8(packet->data + offsetNet);
        netVersion = byte >> 4;
        if (netVersion != 4)
        {
            /* not ipv4 */
            continue;
        }

        netHdrLen = (byte&0xf) << 2;
        
        transProtocol = GetU8(packet->data + offsetNet + 9);
        if (transProtocol != TRANS_UDP)
        {
            /* not UDP */
            continue;
        }
        
        dstIP = GetBE32(packet->data + offsetNet + 16);
        if (dstIP != DST_IP)
        {
            /* not target IP */
            continue;
        }

        offsetTrans = offsetNet + netHdrLen;
        offsetApp = offsetTrans + 8;
        
        dstPort = GetBE16(packet->data + offsetTrans + 2);
        RTPPacketT *rtpPacket = NULL;
        if (dstPort == DST_PORT_DATA)
        {
            uint8_t *data;
            int dataLen = packet->len - offsetApp;
            data = Palloc(pool, dataLen);
            memcpy(data, packet->data + offsetApp, dataLen);
            rtpPacket = RTPPacketCreate(pool, data, dataLen, seqExpect);
                                            
//            LogD("RTP-Data: seq(%u) ", rtpPacket->seqNumU16);
            
            if (rtpPacket->seqNumU16 != seqExpect && seqExpect != 0)
            {
                LogE("drop packet (%hu - %hu) ", seqExpect, rtpPacket->seqNumU16 - 1);
            }
            seqExpect = rtpPacket->seqNumU16 + 1;

            if (rtpPacket->seqNumU16 >= TC_SEQ_BEGIN &&  rtpPacket->seqNumU16 <= TC_SEQ_END)
            {
                dataPacket[indexData++] = rtpPacket;
            }
        }
        else if (dstPort == DST_PORT_FEC)
        {

            FecInfoT fecInfo;
            uint8_t *data;
            
            int dataLen = packet->len - offsetApp;
            data = Palloc(pool, dataLen);
            memcpy(data, packet->data + offsetApp, dataLen);
            rtpPacket = RTPPacketCreate(pool, data, dataLen, 0);

            parseFecPacket(rtpPacket, &fecInfo);
            LogD("RTP-Fec: seq(%u), %hhu/%hhu,  %hu - %hu ", rtpPacket->seqNumU16, 
                fecInfo.redundIdx, fecInfo.redundNum, fecInfo.rtpBeginSeq, fecInfo.rtpEndSeq);

            if (fecInfo.rtpBeginSeq == TC_SEQ_BEGIN && fecInfo.rtpEndSeq == TC_SEQ_END)
            {
                fecPacket[indexFec++] = rtpPacket;
            }
            
        }
        else
        {
            LogD("Not RTP-Packet ");
            /* not RTP packet... */
            continue;
        }

        if (indexData == 100 && indexFec == 5)
        {
            LogI("All Packet ready.");
            break;
        }
        
    }

#if 0
    ret = TC_DropNull(pool, dataPacket, fecPacket);
    LogI("TC_DropNull: %s", ret == 0 ? "Success" : "Fail");

    ret = TC_DropDataNo2(pool, dataPacket, fecPacket);
    LogI("TC_DropDataNo2: %s", ret == 0 ? "Success" : "Fail");
#endif

    ret = TC_RSDecodeRFW(dataPacket, fecPacket);
    LogI("TC_DropDataNo2: %s", ret == 0 ? "Success" : "Fail");

    LogI("The End.");
    return 0;
}

